home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Ken Long / NewMaxwell-c / Original / damain.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-04  |  9.7 KB  |  481 lines  |  [TEXT/KAHL]

  1. /*
  2.  * Maxwell: particles in box, with gate in middle.  Inspired by the
  3.  * demo by the same name for the Teletype DMD 5620 terminal.
  4.  */
  5.  
  6. #include <acc.h>
  7. #include <desk.h>
  8. #include <qd.h>
  9. #include <qdvars.h>
  10. #include <event.h>
  11. #include <res.h>
  12. #include <misc.h>
  13. #include <mem.h>
  14. #include <menu.h>
  15. #include <te.h>
  16. #include <font.h>
  17. #include <file.h>
  18. #include <win.h>
  19. #include <control.h>
  20. #include <device.h>
  21. #include "ball.h"
  22.  
  23. #define    NULL    0
  24.  
  25. ACC( 0x2400, 2, 0xfffb, 0, 7, "Maxwell" )
  26.  
  27. #define MAXBALLS    20                /* must be even */
  28.  
  29. ball Balls[ MAXBALLS ];
  30. int nBalls;
  31. int GateState;
  32. ControlHandle help;
  33.  
  34. int wide = 300;                        /* initial size of box */
  35. int tall = 255;
  36.  
  37. accopen( dctl, pb )
  38.     dctlentry *dctl;
  39.     paramblockrec *pb;
  40. {
  41.     WindowPeek mywindow;
  42.     Rect bound; int i;
  43.             
  44.     if ( dctl->dCtlWindow == NULL )
  45.     {
  46.         GrafPtr saveport;
  47.         
  48.         GetPort( &saveport );
  49.         SetRect( &bound, 100, 40, 130 + wide, 80 + tall );
  50.         mywindow = NewWindow( 0L, &bound, "Maxwell", -1,
  51.             DocumentProc, -1L, -1, 0L );
  52.         mywindow->windowKind = dctl->dCtlRefNum;
  53.         dctl->dCtlWindow = mywindow;
  54.         initmove();
  55.         SetPort( mywindow );
  56.         helpControl( mywindow );
  57.         InitRandom( TickCount() );
  58.         SetupUniverse();
  59.         SetPort( saveport );
  60.     }
  61.     return 0;
  62. }
  63.  
  64. accclose( dctl, pb )
  65.     dctlentry *dctl;
  66.     paramblockrec *pb;
  67. {
  68.     DisposeWindow( dctl->dCtlWindow );
  69.     dctl->dCtlWindow = NULL;
  70.     return 0;
  71. }
  72.  
  73. accctl( dctl, pb )
  74.     dctlentry *dctl;
  75.     paramblockrec *pb;
  76. {
  77.     WindowPtr mywindow;
  78.     int code;
  79.     EventRecord *erp;
  80.     int infront, inmine;
  81.     
  82.     code = pb->paramunion.CntrlParam.CSCode;
  83.     mywindow = dctl->dCtlWindow;
  84.     
  85.     SetPort( mywindow );
  86.  
  87.     infront = mywindow == FrontWindow();
  88.     
  89.     switch ( code ) {
  90. case accRun:
  91.         step( mywindow, infront );
  92.         break;
  93. case accEvent:
  94.         erp = (EventRecord *)(pb->paramunion.CntrlParam.csParam.eventaddr);
  95.         switch ( erp->what ) {
  96.     case activateEvt:
  97.     case updateEvt:
  98.             setgate(0);
  99.             redraw( mywindow );
  100.             HiliteControl( help, infront ? 0 : 255 );
  101.             break;
  102.     case mouseDown:
  103.             /*
  104.              * Only mouse down events we care about are in the grow
  105.              * icon and in the help button.  I tried FindWindow(),
  106.              * but it refused to distinguish between the grow icon
  107.              * and the rest of the content region, so instead, I
  108.              * will just do it directly with co-ordinates.
  109.              */
  110.             if ( infront ) {
  111.                 GlobalToLocal( &erp->where );
  112.                 /*
  113.                  * check grow icon...
  114.                  */
  115.                 if ( 15+wide < erp->where.a.h
  116.                   && erp->where.a.h < 30+wide
  117.                   && 25+tall < erp->where.a.v &&
  118.                   erp->where.a.v < 40+tall
  119.                 ) {
  120.                     LocalToGlobal( &erp->where );
  121.                     resize( mywindow, &erp->where );
  122.                 } else
  123.                 /*
  124.                  * see if there is any reason to check the help button...
  125.                  */
  126.                 if ( erp->where.a.v <= 25 )
  127.                     /*
  128.                      * yup, there is...
  129.                      */
  130.                     checkhelp( erp, mywindow );
  131.             }
  132.             break;
  133.         }
  134.         break;
  135.     }
  136. }
  137.  
  138. accprime() {}
  139. accstatus() {}
  140.  
  141. /*
  142.  * step() moves all the balls
  143.  */
  144. static int nslow;
  145.  
  146. step(wp, infront)
  147.     GrafPtr wp;
  148. {
  149.     register int i,j;
  150.  
  151.     nslow = 0;
  152.     sortballs();
  153.     for ( i = 0; i < nBalls; i++ ) {
  154.         for ( j = i+1; j < nBalls; j++ )
  155.             if ( bbump( Balls+i, Balls+j ) )
  156.                 break;
  157.         wbump( Balls+i );
  158.         if ( infront )
  159.             setgate( Button() );
  160.     }
  161.     /*
  162.      * If we just draw the balls in order on the screen it will look
  163.      * bad, since we have them sorted by x.
  164.      */
  165.     for ( i = 0; i < 10; i++ )
  166.     for ( j = i; j < nBalls; j += 10 )
  167.         MoveBall( Balls + j, wp );
  168.  
  169.     if ( nslow < nBalls/3 )
  170.         walls( -1 );
  171.     else
  172.     if ( nslow >= (nBalls/3)<<1 )
  173.         walls( 1 );
  174.     else
  175.         walls( 0 );
  176. }
  177.  
  178. MoveBall( pA, wp )
  179.     register ball *pA;
  180.     GrafPtr wp;
  181. {
  182.     Rect R;
  183.     register long v2;
  184.     ball before;
  185.     
  186.     before = *pA;        /* save old ball */
  187.     mball( pA );        /* compute new ball */
  188.     
  189.     v2 = pA->vx * pA->vx + pA->vy * pA->vy;
  190.     
  191.     if ( v2 <= SLOW ) nslow++;
  192.     
  193.     pA->pict = (v2 <= SLOW ? 0 : 1);
  194.     
  195.     Draw( wp, &before, pA );
  196.     
  197.     return;
  198. }
  199.  
  200.  
  201. /*
  202.  * redraw() is called to deal with update events in our window
  203.  */
  204. redraw( wp )
  205.     GrafPtr wp;
  206. {
  207.     Rect bound; int i;
  208.  
  209.     BeginUpdate( wp );
  210.     SetRect( &bound, 0, 0, wide+30, tall+40 );
  211.     EraseRect( &bound );                /* clear whole window */
  212.     SetRect( &bound, 15, 25, 15+wide, 25+tall );
  213.     FrameRect( &bound );                /* draw outer border */
  214.     SetRect( &bound, 13+wide/2, 25, 17+wide/2, 25+tall );
  215.     InvertRect( &bound );                /* draw barrier */
  216.     SetRect( &bound, 13+wide/2, 24+tall/3, 17+wide/2, 26+(tall+tall)/3 );
  217.     InvertRect( &bound );                /* remove gate + 1 pixel */
  218.     Toggle();
  219.     GateState = 0;                        /* gate is closed */
  220.     DrawControls( wp );
  221.     
  222.     /*
  223.      * Now draw the balls.  This only works for an even number of balls,
  224.      * since Draw() wants two balls.
  225.      */
  226.     for ( i = 0; i < nBalls; i+=2 )
  227.         Draw( wp, Balls + i, Balls + i + 1 );
  228.  
  229.     EndUpdate( wp );
  230.  
  231.     /*
  232.      * now do the grow icon
  233.      */
  234.     SetRect( &bound, wide+15, tall+25, wide+30, tall+40 );
  235.     ClipRect( &bound );
  236.     DrawGrowIcon( wp );
  237.     SetRect( &bound, 0, 0, 31415, 27182 );
  238.     ClipRect( &bound );
  239. }
  240.  
  241. /*
  242.  * Draw() draws two balls in the window.  It is used both to draw the
  243.  * initial balls, with the two balls being different balls, and to
  244.  * draw a ball that has moved.  In the latter case, the two balls are
  245.  * the same ball, once at the old position and once at the new postion.
  246.  * This works since drawing is done in xor mode.
  247.  */
  248. Draw( wp, pA, pB )
  249.     GrafPtr wp;
  250.     register ball *pA, *pB;
  251. {
  252.     register long ax, ay, bx, by;
  253.     
  254.     ax = pA->x >> 2;
  255.     ay = pA->y >> 2;
  256.     
  257.     bx = pB->x >> 2;
  258.     by = pB->y >> 2;
  259.     
  260.     movebits( wp, bx-GRAD+15, by-GRAD+25, ax-GRAD+15,
  261.         ay-GRAD+25, (long)pB->pict, (long)pA->pict );
  262. }
  263.  
  264. /*
  265.  * set gate to a given state.  bs != 0 means make sure the gate is
  266.  * open, and bs == 0 means make sure it is closed.
  267.  */
  268. setgate( bs ) {
  269.     if ( !!bs != GateState ) {
  270.         GateState = !!bs;
  271.         Toggle();
  272.     }
  273. }
  274.  
  275. /*
  276.  * Change the state of the gate on the screen
  277.  */
  278. Toggle() {
  279.     Rect bound;
  280.  
  281.     SetRect( &bound, 13+wide/2, 25+tall/3, 17+wide/2, 25+(tall+tall)/3 );
  282.     InvertRect( &bound );
  283. }
  284.  
  285. InitRandom( seed )
  286.     long seed;
  287. {
  288.     asm {
  289.         move.l    (A5), A0
  290.         move.l    seed(A6), 0xff82(A0)
  291.     }
  292. }
  293.  
  294. /*
  295.  * Generate a random integer in [low,high].  If "contract" is not zero,
  296.  * it skews the distribution to favor numbers nearer the center of
  297.  * the interval
  298.  */
  299. rani(low,high,contract)
  300.     int low, high;
  301. {
  302.     register long r;
  303.     register int range;
  304.     
  305.     r = (Random()>>1) + 16384;
  306.     if ( !contract )
  307.         return r * (high-low) / 32768L + low;
  308.     
  309.     range = (high - low) >> 1;
  310.     
  311.     r = r * range;
  312.     r /= 32768;
  313.     r *= Random();
  314.     range = r / 32768;
  315.     
  316.     return ( (low + high) >> 1 ) + range;
  317. }
  318.  
  319. /*
  320.  * Set up balls.
  321.  */
  322. SetupUniverse() {
  323.     int i; long nb;
  324.     
  325.     PenNormal();
  326.     PenMode( patXor );
  327.     
  328.     nb = (long)tall * (long)wide; nb >>= 13; nb <<= 1;
  329.     nBalls = nb + 4;
  330.     if ( nBalls > MAXBALLS )
  331.         nBalls = MAXBALLS;
  332.  
  333.     for ( i = 0; i < nBalls; i++ ) {
  334.         register long a,b;
  335.         
  336.         Balls[i].x  = rani(CRAD, (wide<<2)-CRAD, 0);
  337.         Balls[i].y  = rani(CRAD, (tall<<2)-CRAD, 0);
  338.         Balls[i].vx = a = rani(-40, 40, 1);
  339.         Balls[i].vy = b = rani(-40, 40, 1);
  340.         if ( a*a + b*b <= SLOW )
  341.             Balls[i].pict = 0;
  342.         else
  343.             Balls[i].pict = 1;
  344.     }
  345. }
  346.  
  347. /*
  348.  * resize the window
  349.  */
  350. resize( wp, mp )
  351.     WindowPtr wp;
  352.     Point *mp;
  353. {
  354.     Rect sizerect; long result;
  355.     
  356.     SetRect( &sizerect, 150, 80, 2500, 402 );
  357.     result = GrowWindow( wp, mp, &sizerect );
  358.     if ( !result )
  359.         return;
  360.     tall = result >> 16; tall &= 0xffff;
  361.     wide = result & 0xffff;
  362.     
  363.     wide -= 29; wide -= wide % 2;
  364.     tall -= 38; tall -= tall % 3;
  365.     
  366.     DisposeControl( help );
  367.     
  368.     SizeWindow( wp, wide + 30, tall + 40, 0 );
  369.     
  370.     helpControl( wp );
  371.     
  372.     SetRect( &sizerect, 0, 0, wide + 30, tall + 40 );
  373.     InvalRect( &sizerect );
  374.     
  375.     SetupUniverse();
  376.     redraw( wp );
  377. }
  378.  
  379. /*
  380.  * Sort balls by x co-ordinate.  The first time this is called, it has
  381.  * to do a lot of work, but on subsequent calls, the balls will for the
  382.  * most part be already in order.  We shall use a bubble sort, since
  383.  * it is very fast for an already sorted list.
  384.  */
  385. sortballs() {
  386.     register int i, j, flag;
  387.     
  388.     flag = 1;
  389.     for ( i = 0; i < (nBalls - 1) && flag; i++ )
  390.         for ( flag = 0, j = nBalls-1; j > i; --j )
  391.             if ( Balls[j-1].x > Balls[j].x ) {
  392.                 ball temp;
  393.                 flag = 1;
  394.                 temp = Balls[j-1]; Balls[j-1] = Balls[j];
  395.                 Balls[j] = temp;
  396.             }
  397. }
  398.  
  399. /*
  400.  * repond to a click in the help button
  401.  */
  402. showhelp() {
  403.     Rect bound;
  404.     WindowPtr wp;
  405.     char *h1, *h2, *h3, *h4;
  406.     char helptext[1024];
  407.     
  408.     SetRect( &bound, 91, 68, 421, 303 );
  409.     wp = NewWindow( 0L, &bound, "A", -1, DBoxProc, -1L, 0, 0L );
  410.     SetPort( wp );
  411.  
  412.     TextMode( srcXor );
  413.     TextSize( 9 );
  414.     
  415.     SetRect( &bound, 10, 10, 320, 225 );
  416.  
  417.     h1 = "\
  418. Maxwell V2.1 from Mithral Engineering\r\r\
  419. Whenever the Maxwell window is in front, holding down the\
  420.  mouse button opens the gate so that balls may go from one\
  421.  side to the other.\r\r";
  422.      h2 = "\
  423. Try to get all the fast balls ( the black ones ) in the right\
  424.  half of the window, and all the slow ones in the left.\r\r\
  425. Due to roundoff errors, at each collision there is a slight";
  426.     h3 = "\
  427.  net decrease in the total energy of the balls.  To balance\
  428.  this, the right wall will become 'hot' if less than one third\
  429.  of the balls are fast balls.  When a slow ball hits the hot right";
  430.      h4 = "\
  431.  wall, it will become a very fast ball.  When more than one third\
  432.  of the balls are fast, the right wall will cool off.\r\r\
  433. This program and its source code are in the public domain";
  434.  
  435.     strcpy( helptext, h1 );
  436.     strcat( helptext, h2 );
  437.     strcat( helptext, h3 );
  438.     strcat( helptext, h4 );
  439.  
  440.     TextBox( helptext, (long)strlen( helptext ), &bound, teJustLeft );
  441.     
  442.     while ( !Button() )
  443.         ;
  444.     FlushEvents( mDownMask, 0 );
  445.     DisposeWindow( wp );
  446. }
  447.  
  448. /*
  449.  * put up the help button
  450.  */
  451. helpControl( wp )
  452.     WindowPtr wp;
  453. {
  454.     Rect bound;
  455.     
  456.     SetRect( &bound, (wide>>1)-7, 4 , (wide>>1)+37, 22 );
  457.     
  458.     help = NewControl( wp, &bound, "Info...", -1,0,0,0, PushButProc, 0L );
  459. }
  460.  
  461. /*
  462.  * find out if the user wants help
  463.  */
  464. checkhelp( erp, win )
  465.     EventRecord * erp;
  466.     WindowPtr win;
  467. {
  468.     int part;
  469.     ControlHandle ch;
  470.     
  471.     part = FindControl( &erp->where, win, &ch );
  472.     
  473.     if ( part != inButton || ch != help )
  474.         return;
  475.         
  476.      part = TrackControl( ch, &erp->where, 0L );
  477.     
  478.      if ( part == inButton )
  479.          showhelp();
  480. }
  481.